package com.i72.penetrate.tools;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.i72.basic.consts.Consts;
import com.i72.basic.tools.BeanHelper;
import com.i72.penetrate.web.ResponseFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang3.ClassUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
public class HttpRequestHelper extends ServletRequestAttributes {

    /**
     * 请求和响应格式
     */
    private final static String CONTENT_TYPE_JSON = "application/json";
    private final static String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
    private final static String CONTENT_TYPE_HTML = "text/html";
    private final static String RESPONSE_CONTENT_TYPPE="application/json;charset=utf-8";
    private final static String METHOD_POST = "post";
    private final static String METHOD_GET = "get";



    //@Autowired
    //private HttpServletRequest request;

    //@Autowired
    //private HttpServletResponse response;

    /*
    public static HttpRequestHelper build(HttpServletRequest request,HttpServletResponse response){
        return new HttpRequestHelper(request,response);
    }*/


    //public HttpRequestHelper(){}


    public HttpRequestHelper(HttpServletRequest request, HttpServletResponse response){
        super(request,response);
    }


    public String getValidateMessage(){
        return (String)this.getRequest().getAttribute(Consts.VALIDATE_FORM_ERROR_MESSAGE);
    }

    public String getController(){


        String path = this.getRequest().getPathInfo();

        String [] paths = path.replaceFirst("/","").split("/");

        if(paths!=null && paths.length==2){
            return paths[0];
        }

        //log.info(this.getRequest().getRequestURI()+"不是合法的url,没有解析到controller");
        return "";
    }

    public String getAction(){

        String path = this.getRequest().getPathInfo();

        String [] paths = path.replaceFirst("/","").split("/");

        if(paths!=null && paths.length==2){
            return paths[1];
        }

        //log.info(this.getRequest().getRequestURI()+"不是合法的url,没有解析到controller");
        return "";
    }



    /**
     * 从请求解析为action调用参数
     * @param m
     * @return
     */
    public Object[] getParam(Method m){

        Object param = null;
        List<Object> params = new ArrayList<>();
        String method = this.getRequest().getMethod().toLowerCase();
        Map formMap = null;

        if(m.getParameterCount()>0) {

            if (method.equals(METHOD_GET)) {
                formMap = getQueryMap();

            } else if (method.equals(METHOD_POST)) {

                if(this.getRequest().getContentType().equals(CONTENT_TYPE_JSON)) {

                    try {

                        String result = new BufferedReader(new InputStreamReader(this.getRequest().getInputStream()))
                                .lines().collect(Collectors.joining(System.lineSeparator()));
                        Object o = JSON.parse(result);
                        JSONObject jsonObject = (JSONObject)o;

                        formMap = (Map)jsonObject.getInnerMap();
                    }catch (Exception e){

                    }
                }else {
                    formMap = this.getRequest().getParameterMap();
                }

            }

            if(formMap!=null && formMap.size()>0){

                Map m2 = formMap;
                formMap = new LinkedCaseInsensitiveMap();
                formMap.putAll(m2);

                //转换为不区分大小的map
                //因为url是不区分大小写的
            }else{
                formMap = new LinkedCaseInsensitiveMap();
            }

            log.info("http请求参数:"+JSON.toJSONString(formMap,SerializerFeature.WriteMapNullValue));

            Parameter [] ps = m.getParameters();
            Annotation  annotations = null;
            if(ps.length==1){
                annotations = ps[0].getAnnotation(RequestBody.class);
                if(annotations!=null){
                    if (formMap != null && formMap.keySet().size() > 0) {
                        String json = JSON.toJSONString(formMap);
                        param = JSON.parseObject(json,ps[0].getType());
                        return new Object[]{param};
                    }
                }
            }

            //form处理
            for(Parameter p:ps){
                param = null; //没有值时null补位。总之不会少参数
                if(!isNestedObject(p.getType())){
                    //只处理串，包装类型，基本类型
                    //复杂类型其实是逼着你用 RequestBody
                    //不区分大小写查找
                    //Optional<String> find = formMap.keySet().stream().filter(i->i.toString().toLowerCase().equals(p.getName())).findFirst();

                    //String key = find.orElse("");

                    String paramName = "";

                    annotations = p.getAnnotation(RequestParam.class);

                    if(annotations!=null){

                        Map<String,List<Object>> value = BeanHelper.getAnnotationValue(annotations);

                        List<Object> pNameList = value.get("value");
                        if(pNameList!=null && pNameList.size()>0){
                            paramName = pNameList.get(0).toString();
                        }


                    }else{
                        paramName = p.getName();
                        /*
                        * JDK8新特性:编译器保留方法参数名字
                        * 如果没有设置参数可能就会失去参数名字。
                        * 所以最好实用@RequestParam注解
                        * */
                    }

                    if(formMap.containsKey(paramName)){
                        Object v = formMap.get(paramName);
                        if(!StringUtils.isEmpty(v)){
                             param = ConvertUtils.convert(v,p.getType());
                        }
                    }

                }


                params.add(param);
            }
        }

        return params.toArray();
    }

    private boolean isNestedObject(Class c){

        //是否嵌套类型【非 基本类型，非包装类型，非String】
       if(c.equals(String.class) || c.isPrimitive() || ClassUtils.wrapperToPrimitive(c) != null){
           return false;
       }
       return true;
    }

    /**
     * 将调用转换为http调用参数
     * @param m
     * @return
     */
    public Object getHttpParam(Method m,Object ... params){

        //可能是 object
        //可能是 map

        //根据 @RequestBody和@RequestParam决定

        Parameter [] parameters = m.getParameters();
        Annotation annotation = null;
        if(params!=null && params.length>0) {
            if(params.length==1){
                annotation = parameters[0].getAnnotation(RequestBody.class);
                if(annotation!=null){
                    return params[0];
                }
            }
            Map mm = new HashMap();
            for(int i=0;i<parameters.length;i++){
                Parameter p = parameters[i];
                annotation = p.getAnnotation(RequestParam.class);
                if(annotation!=null){
                    Map<String,List<Object>> value = BeanHelper.getAnnotationValue(annotation);

                    List<Object> pNameList = value.get("value");
                    if(pNameList!=null && pNameList.size()>0){
                        String paramName = pNameList.get(0).toString();
                        if(!StringUtils.isEmpty(paramName)){
                            mm.put(paramName,params[i]);
                        }
                    }
                }
            }

            return mm;
        }
        return null;

    }
    private Map getQueryMap(){

        String query = this.getRequest().getQueryString();
        Map m = null;

        String [] querys = null;

        if(!StringUtils.isEmpty(query) && query.contains("=")){
            querys = query.split("&");
        }

        if(querys!=null && querys.length>0){
            m = new HashMap();
            for(String q:querys){
                if(!StringUtils.isEmpty(q)){
                    String [] values = q.split("=");
                    if(values!=null && values.length>=2){
                        m.put(values[0],values[1]);
                    }
                }
            }
        }

        return m;
    }



    public void write(Object data,String message)throws IOException {

        ResponseFormat responseFormat = new ResponseFormat();

        responseFormat.setData(data);
        if(!StringUtils.isEmpty(message)) {
            responseFormat.setMessage(message);
            responseFormat.setSuccess(false);
        }
        this.getResponse().setCharacterEncoding("UTF-8");
        this.getResponse().setContentType(RESPONSE_CONTENT_TYPPE);
        this.getResponse().getWriter().println(JSON.toJSONString(responseFormat, SerializerFeature.WriteMapNullValue));
        this.getResponse().getWriter().flush();

    }

    public void error() throws IOException{
        write(null,"调用出现问题，请确认后重试");
    }

    public void http404() throws IOException{
        write(null,"非法请求，不符合请求规范");
    }

    public boolean validateRequest(){


        if(this.getRequest().getRequestURI().contains("favicon.ico")){
            return false;
        }

        String method = this.getRequest().getMethod().toLowerCase();
        String contentType = this.getRequest().getContentType();
        String methods = METHOD_GET+"|"+METHOD_POST;
        String contentTypes = CONTENT_TYPE_JSON+"|"+CONTENT_TYPE_FORM+"|"+CONTENT_TYPE_HTML;





        if(!methods.contains(method)){
            return false;
        }

        if(!StringUtils.isEmpty(contentType) && !contentTypes.contains(contentType)){
            return false;
        }


        return true;

    }

}
